﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;

namespace NetTopologySuite.Noding
{
	public class NodingValidator
	{
		public NodingValidator(IList<ISegmentString> segStrings)
		{
			this._segStrings = segStrings;
		}

		public void CheckValid()
		{
			this.CheckEndPtVertexIntersections();
			this.CheckInteriorIntersections();
			this.CheckCollapses();
		}

		private void CheckCollapses()
		{
			foreach (ISegmentString ss in this._segStrings)
			{
				NodingValidator.CheckCollapses(ss);
			}
		}

		private static void CheckCollapses(ISegmentString ss)
		{
			Coordinate[] coordinates = ss.Coordinates;
			for (int i = 0; i < coordinates.Length - 2; i++)
			{
				NodingValidator.CheckCollapse(coordinates[i], coordinates[i + 1], coordinates[i + 2]);
			}
		}

		private static void CheckCollapse(Coordinate p0, Coordinate p1, Coordinate p2)
		{
			if (p0.Equals(p2))
			{
				throw new ApplicationException(string.Format("found non-noded collapse at: {0}, {1} {2}", p0, p1, p2));
			}
		}

		private void CheckInteriorIntersections()
		{
			foreach (ISegmentString ss in this._segStrings)
			{
				foreach (ISegmentString ss2 in this._segStrings)
				{
					this.CheckInteriorIntersections(ss, ss2);
				}
			}
		}

		private void CheckInteriorIntersections(ISegmentString ss0, ISegmentString ss1)
		{
			Coordinate[] coordinates = ss0.Coordinates;
			Coordinate[] coordinates2 = ss1.Coordinates;
			for (int i = 0; i < coordinates.Length - 1; i++)
			{
				for (int j = 0; j < coordinates2.Length - 1; j++)
				{
					this.CheckInteriorIntersections(ss0, i, ss1, j);
				}
			}
		}

		private void CheckInteriorIntersections(ISegmentString e0, int segIndex0, ISegmentString e1, int segIndex1)
		{
			if (e0 == e1 && segIndex0 == segIndex1)
			{
				return;
			}
			Coordinate coordinate = e0.Coordinates[segIndex0];
			Coordinate coordinate2 = e0.Coordinates[segIndex0 + 1];
			Coordinate coordinate3 = e1.Coordinates[segIndex1];
			Coordinate coordinate4 = e1.Coordinates[segIndex1 + 1];
			this._li.ComputeIntersection(coordinate, coordinate2, coordinate3, coordinate4);
			if (this._li.HasIntersection && (this._li.IsProper || NodingValidator.HasInteriorIntersection(this._li, coordinate, coordinate2) || NodingValidator.HasInteriorIntersection(this._li, coordinate3, coordinate4)))
			{
				throw new ApplicationException(string.Format("found non-noded intersection at {0}-{1} and {2}-{3}", new object[]
				{
					coordinate,
					coordinate2,
					coordinate3,
					coordinate4
				}));
			}
		}

		private static bool HasInteriorIntersection(LineIntersector li, Coordinate p0, Coordinate p1)
		{
			for (int i = 0; i < li.IntersectionNum; i++)
			{
				Coordinate intersection = li.GetIntersection(i);
				if (!intersection.Equals(p0) && !intersection.Equals(p1))
				{
					return true;
				}
			}
			return false;
		}

		private void CheckEndPtVertexIntersections()
		{
			foreach (ISegmentString segmentString in this._segStrings)
			{
				Coordinate[] coordinates = segmentString.Coordinates;
				NodingValidator.CheckEndPtVertexIntersections(coordinates[0], this._segStrings);
				NodingValidator.CheckEndPtVertexIntersections(coordinates[coordinates.Length - 1], this._segStrings);
			}
		}

		private static void CheckEndPtVertexIntersections(Coordinate testPt, IEnumerable<ISegmentString> segStrings)
		{
			foreach (ISegmentString segmentString in segStrings)
			{
				Coordinate[] coordinates = segmentString.Coordinates;
				for (int i = 1; i < coordinates.Length - 1; i++)
				{
					if (coordinates[i].Equals(testPt))
					{
						throw new ApplicationException(string.Format("found endpt/interior pt intersection at index {0} :pt {1}", i, testPt));
					}
				}
			}
		}

		private readonly LineIntersector _li = new RobustLineIntersector();

		private readonly IList<ISegmentString> _segStrings;
	}
}
